/******** 虚拟表 ********/
/*
ver : 1.0
Created by donkey on 2019/8/21.
*/

import { debounce } from 'lodash'
import ResizeObserver from 'resize-observer-polyfill'

import wrap from './_wrap.js'

export default (Vue) => {
    return Vue.directive('virtual-table', {
        bind(el, binding, vnode) {

            let resizeObserver
            let flag
            let tableData     // 表格需要载入的原始数据
            let vData         // 动态载入的虚拟表数据
            let trHeight      // 表格tr的高度
            let loading       // loading动画
            let visibleCount  // 可视区域的内容条数
            let tableFixed    // el-table 表格dom
            let tableFixedR   // el-table 表格右侧锁列
            let tableFixedL   // el-table 表格左侧锁列
            let offset = 1    // 加载表格数据偏移量

            let vm = vnode.componentInstance
            let scrollDom = el.querySelector('.el-table__body-wrapper') // el-table能够出现滚动条的dom
            let oldScrollTop = 0

            // 分步加载表格数据的方法
            const updateVisibleData = (e) => {

                // 横向滚动就什么都不做
                if (oldScrollTop === e.target.scrollTop) return false
                oldScrollTop = e.target.scrollTop

                // 取得可见区域的起始数据索引
                let start = Math.floor(e.target.scrollTop / trHeight)

                // 取得可见区域的结束数据索引
                let end = start + visibleCount

                // 给数据加入一个偏移量，以免出现滚动条消失情况
                end < tableData.length ? end += offset : start -= offset

                // 计算出可见区域对应的数据，让 Vue 更新
                // 为了让vue能够监听到数组变化，所采用的变通赋值方式
                vData.splice(0, vData.length)
                let data = tableData.slice(start, end)
                data.forEach((item, index) => {
                    item.itemIndex = start + index + 1
                })
                vData.push(...data)

                // 把可见区域的 top 设置为起始元素在整个列表中的位置（使用 transform 是为了更好的性能）
                let pos = `translate3d(0, ${ start * trHeight }px, 0)`
                tableFixed.style.webkitTransform = pos
                if (tableFixedR) tableFixedR.style.webkitTransform = pos
                if (tableFixedL) tableFixedL.style.webkitTransform = pos

            }

            // 启动器
            const launcher = () => {

                scrollDom.removeEventListener('scroll', updateVisibleData, false)

                // 获取dom数据
                trHeight = scrollDom.querySelector('tr').scrollHeight

                // 获取可视区域的内容条数 (+1 的目的是防止不出现滚动条)
                visibleCount = Math.ceil(scrollDom.clientHeight / trHeight) + offset

                // 可视区域显示的条数小于或等于数据条数就不需要再执行下面的操作了
                if (tableData.length <= visibleCount) {
                    // console.log(`小于${ visibleCount }条数据`)
                    if (tableData.length > 1) {
                        for (let i = 1, len = tableData.length; i < len; i++) {
                            tableData[i].itemIndex = i + 1
                            vData.push(tableData[i])
                            loading.close()
                        }
                    }else{
                        tableData[0].itemIndex = 1
                        loading.close()
                    }

                    return false
                }

                // 插入后续数据
                new Promise((resolve) => {
                    // 插入数据并开始监听
                    for (let i = 1; i < visibleCount; i++) {
                        tableData[i].itemIndex = i + 1
                        vData.push(tableData[i])
                        if (i === visibleCount - 1) {
                            resolve()
                        }
                    }
                }).then(() => {

                    // 删除动画
                    loading.close()

                    // 强制更新一下视图，让滚动条显示出来 并插入div撑开滚动条
                    if (scrollDom.clientHeight < tableData.length * trHeight) { // 如果可视区域大于表格总高度就不需要滚动条了

                        let warpHeight = `${ trHeight * tableData.length }px`
                        // console.log(warpHeight)
                        if (scrollDom.firstChild.className !== 'warp-table') {
                            setTimeout(() => {

                                // 获得tableDom节点 添加撑开滚动条的包裹容器
                                tableFixed = scrollDom.firstChild
                                wrap(tableFixed, warpHeight)
                                let table1, table2
                                let patt = /^el-table__fixed.*/

                                // console.log(scrollDom.nextElementSibling.nextElementSibling.className)

                                let scrollDomfixed = scrollDom.nextElementSibling

                                if(!scrollDomfixed.className){
                                     return
                                }
                                scrollDomfixed.className === 'el-table__footer-wrapper' ? scrollDomfixed = scrollDomfixed.nextElementSibling: undefined

                                if (scrollDomfixed && patt.test(scrollDomfixed.className)) {
                                    table1 = scrollDomfixed
                                    tableFixedR = table1.children[1].firstChild
                                    wrap(tableFixedR, warpHeight)
                                }

                                if (table1 && table1.nextElementSibling && patt.test(table1.nextElementSibling.className)) {
                                    table2 = table1.nextElementSibling
                                    tableFixedL = table2.children[1].firstChild
                                    wrap(tableFixedL, warpHeight)
                                }
                            }, 50)
                        }else{
                            el.querySelectorAll('.warp-table').forEach((div) => {
                                // console.dir(div.style.height)
                                div.style.height = warpHeight
                            })
                        }

                        // 开始监听滚动条
                        scrollDom.addEventListener('scroll', updateVisibleData, false)

                        flag = true

                        // 执行重绘
                        vm.doLayout()

                    }

                })
            }

            // 初始化表格
            const init = () => {

                if (!vnode.context.vData || !vnode.componentOptions.propsData.data) {
                    console.warn(`vData未创建！\n请在模板页面data中先初始化vData为一个数组然后赋值给el-table\n例如：<el-table :data="vData" ...`)
                    return false
                }

                // 初始化vData
                vnode.context.vData = []
                vData = vnode.context.vData

                // 先插入一条数据，方便后面获取当前tr的高度
                tableData[0].itemIndex = 1
                vData.push(tableData[0])

                vm.$nextTick(() => {
                    launcher()
                })

            }

            // 监听数据变化后执行虚拟表方法
            vnode.context.$watch(binding.rawName.split(':')[1], function(newVal) {
                if (newVal && newVal.length > 0) {
                    // 复制后执行
                    tableData = newVal

                    // loding 动画
                    loading = window.ELEMENT.Loading.service({
                        lock: true,
                        background: 'rgba(255, 255, 255, 1)',
                        target: el,
                        spinner: 'el-icon-loading'
                    })
                    setTimeout(() => {
                        init()
                    }, 50)
                }else{
                    // 当遇到置空操作时
                    vData && vData.splice(0, vData.length)
                }
            }, {
                immediate: true
                // deep: true
            })

            // 当监听到父级容器尺寸变后执行一次虚拟数据重载
            const reload = debounce(function() { launcher() }, 100)
            if (!resizeObserver) {
                resizeObserver = new ResizeObserver(() => {
                    if (flag) {
                        reload()
                    }
                })
                resizeObserver.observe(el)
            }

        }
    });
}
